package cool.taomu.toolkit.redisson

import com.google.inject.Inject
import java.util.concurrent.TimeUnit
import org.aopalliance.intercept.MethodInterceptor
import org.aopalliance.intercept.MethodInvocation
import org.redisson.api.RLock
import org.redisson.api.RedissonClient

class RedissonInterceptor implements MethodInterceptor {
    @Inject
    RedissonClient redissonClient;

    override Object invoke(MethodInvocation invocation) throws Throwable {
        var method = invocation.getMethod();
        if (method.isAnnotationPresent(DistributedLock)) {
            var lockAnnotation = method.getAnnotation(DistributedLock);

            var lockKey = evaluateParams(invocation, lockAnnotation.key());

            var RLock lock = redissonClient.getLock(lockKey);
            var boolean isLocked = false;

            try {
                isLocked = lock.tryLock(lockAnnotation.waitTime(), lockAnnotation.leaseTime(), TimeUnit.SECONDS);
                if (!isLocked) {
                    throw new RuntimeException("无法获取分布式锁: " + lockKey);
                }
                return invocation.proceed();
            } finally {
                if (isLocked && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            return invocation.proceed();
        }
    }

    /**
     * 使用 @Param 注解提取参数名和值进行替换
     */
    def String evaluateParams(MethodInvocation invocation, String rawKey0) {
        var method = invocation.getMethod();
        var args = invocation.getArguments();
        var rawKey = rawKey0
        var parameters = method.getParameters();
        for (var int i = 0; i < parameters.length; i++) {
            var paramAnnotation = parameters.get(i).getAnnotation(Param);
            if (paramAnnotation !== null) {
                var String paramName = paramAnnotation.value();
                var Object paramValue = args.get(i);
                rawKey = rawKey.replace("#" + paramName, paramValue.toString());
            }
        }

        return rawKey;
    }

}
